﻿Imports System
Imports System.Linq
Imports System.Windows
Imports System.Windows.Media.Animation
Imports Thriple.Easing
Imports MvvmFoundation.Wpf

''' <summary>
''' A visual arrow element that connects two nodes in a graph.
''' </summary>
Public Class NodeConnector
    Inherits ArrowLine

#Region "Constructor"

    Public Sub New(ByVal startNode As Node, ByVal endNode As Node)

        _startNode = startNode
        _endNode = endNode

        Me.SetIsPartOfCircularDependency()
        Me.SetToolTip()
        Me.UpdateLocations(False)

        _startObserver = New PropertyObserver(Of Node)(_startNode)
        _startObserver.RegisterHandler(Function(n) n.LocationX, Sub(n) Me.UpdateLocations(True))
        _startObserver.RegisterHandler(Function(n) n.LocationY, Sub(n) Me.UpdateLocations(True))
        _startObserver.RegisterHandler(Function(n) n.NodeWidth, Sub(n) Me.UpdateLocations(True))
        _startObserver.RegisterHandler(Function(n) n.NodeHeight, Sub(n) Me.UpdateLocations(True))

        _endObserver = New PropertyObserver(Of Node)(_endNode)
        _endObserver.RegisterHandler(Function(n) n.LocationX, Sub(n) Me.UpdateLocations(True))
        _endObserver.RegisterHandler(Function(n) n.LocationY, Sub(n) Me.UpdateLocations(True))
        _endObserver.RegisterHandler(Function(n) n.NodeWidth, Sub(n) Me.UpdateLocations(True))
        _endObserver.RegisterHandler(Function(n) n.NodeHeight, Sub(n) Me.UpdateLocations(True))

    End Sub

#End Region ' Constructor

#Region "IsPartOfCircularDependency (DP)"

    'public bool IsPartOfCircularDependency
    '{
    '    get { return (bool)GetValue(IsPartOfCircularDependencyProperty) }
    '    private set { SetValue(IsPartOfCircularDependencyPropertyKey, value) }
    '}

    Public Property IsPartOfCircularDependency As Boolean
        Get
            Return DirectCast(GetValue(IsPartOfCircularDependencyProperty), Boolean)
        End Get
        Set(ByVal value As Boolean)
            SetValue(IsPartOfCircularDependencyPropertyKey, value)
        End Set
    End Property

    Shared ReadOnly IsPartOfCircularDependencyPropertyKey As DependencyPropertyKey =
        DependencyProperty.RegisterReadOnly(
            "IsPartOfCircularDependency",
            GetType(Boolean),
            GetType(NodeConnector),
            New UIPropertyMetadata(False))

    Public Shared ReadOnly IsPartOfCircularDependencyProperty As DependencyProperty = IsPartOfCircularDependencyPropertyKey.DependencyProperty

#End Region ' IsPartOfCircularDependency (DP)

#Region "Private Helpers"

    Shared Function ComputeLocation(ByVal node1 As Node, ByVal node2 As Node) As Point

        ' Initially set the location to the center of the first node.
        Dim loc As New Point With
            {
                .X = node1.LocationX + (node1.NodeWidth / 2),
                .Y = node1.LocationY + (node1.NodeHeight / 2)
            }

        Dim overlapY As Boolean = Math.Abs(node1.LocationY - node2.LocationY) < node1.NodeHeight

        If Not overlapY Then
            Dim above As Boolean = node1.LocationY < node2.LocationY
            If (above) Then
                loc.Y += node1.NodeHeight / 2
            Else
                loc.Y -= node1.NodeHeight / 2
            End If
        End If

        Dim overlapX As Boolean = Math.Abs(node1.LocationX - node2.LocationX) < node1.NodeWidth
        If Not overlapX Then
            Dim left As Boolean = node1.LocationX < node2.LocationX
            If left Then
                loc.X += node1.NodeWidth / 2
            Else
                loc.X -= node1.NodeWidth / 2
            End If
        End If

        Return loc
    End Function

    Sub SetIsPartOfCircularDependency()
        'Me.IsPartOfCircularDependency = _startNode.CircularDependencies.Intersect(_endNode.CircularDependencies).Any()
    End Sub

    Sub SetToolTip()
        Dim toolTipText As String = String.Format("{0} depends on {1}", _startNode.ID, _endNode.ID)

        If (_endNode.NodeDependencies.Contains(_startNode)) Then _
            toolTipText += String.Format("\n{0} depends on {1}", _endNode.ID, _startNode.ID)

        MyBase.ToolTip = toolTipText
    End Sub

    Sub UpdateLocations(ByVal animate As Boolean)

        Dim start As Point = ComputeLocation(_startNode, _endNode)
        Dim end1 As Point = ComputeLocation(_endNode, _startNode)

        If (animate) Then
            MyBase.BeginAnimation(ArrowLine.X1Property, CreateAnimation(MyBase.X1, start.X))
            MyBase.BeginAnimation(ArrowLine.Y1Property, CreateAnimation(MyBase.Y1, start.Y))
            MyBase.BeginAnimation(ArrowLine.X2Property, CreateAnimation(MyBase.X2, end1.X))
            MyBase.BeginAnimation(ArrowLine.Y2Property, CreateAnimation(MyBase.Y2, end1.Y))
        Else
            MyBase.X1 = start.X
            MyBase.Y1 = start.Y
            MyBase.X2 = end1.X
            MyBase.Y2 = end1.Y
        End If
    End Sub

    Shared Function CreateAnimation(ByVal from As Double, ByVal to1 As Double) As AnimationTimeline

        Return New EasingDoubleAnimation With
            {
                .Duration = _Duration,
                .Equation = EasingEquation.ElasticEaseOut,
                .From = from,
                .To = to1
            }
    End Function

#End Region ' Private Helpers

#Region "Fields"

    Shared ReadOnly _Duration As Duration = New Duration(TimeSpan.FromSeconds(1))
    ReadOnly _startNode, _endNode As Node

    ReadOnly _startObserver, _endObserver As PropertyObserver(Of Node)

#End Region ' Fields

End Class
